import * as crypto from 'crypto';
import * as request from 'request-promise';
import { AES, enc } from 'crypto-ts';
import { logger } from './logger';

export default class Server {
  public protocol = 'http';
  public host = '127.0.0.1:3000';
  public version = 1;
  public nickname = '';
  public headImg = '';
  public data = [];
  public username = '';
  public password = '';
  public pin = '';

  private publicKey = '';
  private _id = '';

  constructor(protocol?: 'http' | 'https', host?: string) {
    this.host = host || this.host;
    this.protocol = protocol || this.protocol;
  }

  public async login(username: string, password: string) {
    try {
      this.username = username;
      this.password = password;
      password = crypto.createHash('md5').update(password).digest('hex');
      const result = await request.post(`${ this.protocol }://${ this.host }/login`, {
        body: { username, password },
        json: true,
      });
      if (result.success) {
        this.publicKey = result.publicKey;
        this._id = result._id;
        this.version = result.version;
        this.nickname = result.nickname;
        this.headImg = `${ this.protocol }://${ this.host }/headImg/${ result.headImg }`;
        if (result.data) {
          this.data = JSON.parse(AES.decrypt(result.data, this.password).toString(enc.Utf8));
        } else {
          this.data = [{
            id: 1,
            label: '目录',
            type: 'dir',
            children: [],
          }];
        }
      }
      return result.success;
    } catch (error) {
      logger.log(error);
      return false;
    }
  }

  public loginForPin(pin: string, encrypted: string) {
    try {
      const json = JSON.parse(AES.decrypt(encrypted, pin).toString(enc.Utf8));
      if (!json._id || !json.publicKey || !json.password || !json.version || !json.data) {
        return { success: false };
      }
      this._id = json._id;
      this.password = json.password;
      this.publicKey = json.publicKey;
      this.nickname = json.nickname;
      this.headImg = json.headImg;
      this.version = json.version;
      this.host = json.host;
      this.protocol = json.protocol;
      this.username = json.username;
      this.pin = pin;
      this.data = JSON.parse(AES.decrypt(json.data, this.password).toString(enc.Utf8));
      return true;
    } catch (error) {
      logger.error(error);
      return false;
    }
  }

  public async update(data: string) {
    try {
      const sendData = { data: AES.encrypt(data, this.password).toString() };
      const key = Math.random().toString();
      const encrypted = AES.encrypt(JSON.stringify(sendData), key).toString();
      const result = await request.post(`${ this.protocol }://${ this.host }/update`, {
        body: {
          encrypted,
          _id: this._id,
          key: crypto.publicEncrypt(this.publicKey,  Buffer.from(key)),
        },
        json: true,
      });
      if (result.success) {
        this.data = JSON.parse(data);
        this.version = result.version;
      }
      return result;
    } catch (error) {
      return { success: false };
    }
  }

  public encryptDataForPin() {
    try {
      const data = JSON.stringify({
        _id: this._id,
        publicKey: this.publicKey,
        password: this.password,
        version: this.version,
        nickname: this.nickname,
        headImg: this.headImg,
        username: this.username,
        host: this.host,
        protocol: this.protocol,
        data: AES.encrypt(JSON.stringify(this.data), this.password).toString(),
      });
      return AES.encrypt(data, this.pin).toString();
    } catch (error) {
      return '';
    }
  }
}
